#include <iostream>
#include <fstream>
#include <string>
#include <unordered_map>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <vector>
static size_t Split(const std::string& src,const std::string& sep,std::vector<std::string>* arr) // 字符串分割
    {
        size_t offset = 0;
        while(offset < src.size()){
            size_t pos = src.find(sep,offset);
            if(pos == std::string::npos){
                arr->push_back(src.substr(offset));
                return arr->size();
            }
            if(pos - offset == 0) {
                offset = pos + sep.size();
                continue;
            }
            arr->emplace_back(src.substr(offset,pos-offset));
            offset = pos + sep.size();
        }
        return arr->size();
    }
bool ReadFile(const std::string &filename, std::string *buf) // 读取文件内容放到buffer中
{
    std::ifstream ifs(filename, std::ios::binary);
    if (!ifs.is_open())
    {
        // ERR_LOG("OPEN %s FILE ERR",filename.c_str());
        return false;
    }
    size_t fsize = 0;
    ifs.seekg(0, ifs.end); // 跳转读写位置到末尾
    fsize = ifs.tellg();   // 告知偏移量
    ifs.seekg(0, ifs.beg); // 设置偏移量
    buf->resize(fsize);
    ifs.read(&(*buf)[0], fsize);
    if (!ifs.good())
    {
        // ERR_LOG("READ %s FILE ERR",filename.c_str());
        ifs.close();
        return false;
    }
    ifs.close();
    return true;
}
bool WriteFile(const std::string &filename, const std::string &buf) // 写入数据
{
    std::ofstream ofs(filename, std::ios::binary | std::ios::trunc);
    if (!ofs.is_open())
    {
        // ERR_LOG("OPEN %s FILE ERR",filename.c_str());
        return false;
    }
    ofs.write(buf.c_str(), buf.size());
    if (!ofs.good())
    {
        // ERR_LOG("WRITE %s FILE ERR",filename.c_str());
        ofs.close();
        return false;
    }
    ofs.close();
    return true;
}
std::string UrlEncode(const std::string &url, bool convert_space_to_plus) // encode 避免ERL中资源路径与查询字符串中的特殊字符与HTTP请求中的特殊字符产生歧义
// 编码格式：；将特殊字符的ascii值，转换为两个16禁止的字符 前缀% c++ -> c%20%20
// 不编码的特殊字符：RFC3986 .-_~ 字母数字属于绝对不编码字符
// W3C文档中固定，查询字符串' ' -> + 解码就是 + -> ' '
{
    std::string res;
    for (const auto &ch : url)
    {
        if (ch == '.' || ch == '-' || ch == '_' || ch == '~' || static_cast<bool>(isalnum(ch)))
        {
            res += ch;
            continue;
        }
        else if (ch == ' ' && convert_space_to_plus)
        {
            res += "+";
            continue;
        }
        // 转换为 %HH操作
        char tmp[4] = {0};
        snprintf(tmp, 4, "%%%02X", ch);
        res += tmp;
    }
    return res;
}
char HexToI(char c)
{
    if (isdigit(c))
        return c - '0';
    else if (islower(c))
        return c - 'a' + 10;
    else if (isupper(c))
        return c - 'A' + 10;
    return -1;
}

std::string UrlDecode(const std::string &url, bool convert_space_to_plus) // decode
{
    // 遇到% 将后后面的2个字符转换为数字 nums[0] << 4 + nums[1]
    std::string res;
    for (int i = 0; i < url.size(); i++)
    {
        if (url[i] == '+' && convert_space_to_plus)
        {
            res += ' ';
            continue;
        }
        if (url[i] == '%')
        {
            char v1 = HexToI(url[i + 1]);
            char v2 = HexToI(url[i + 2]);
            char v = (v1 << 4) + v2;
            res += v;
            i += 2;
        }
        else
        {
            res += url[i];
        }
    }
    return res;
}
static std::string StatusDesc(int statu) // 状态码信息的获取
{
    std::unordered_map<int, std::string> _statu_msg = {
        {100, "Continue"},
        {101, "Switching Protocols"},
        {200, "OK"},
        {201, "Created"},
        {202, "Accepted"},
        {203, "Non-Authoritative Information"},
        {204, "No Content"},
        {205, "Reset Content"},
        {206, "Partial Content"},
        {300, "Multiple Choices"},
        {301, "Moved Permanently"},
        {302, "Found"},
        {303, "See Other"},
        {304, "Not Modified"},
        {305, "Use Proxy"},
        {306, "Unused"},
        {307, "Temporary Redirect"},
        {400, "Bad Request"},
        {401, "Unauthorized"},
        {402, "Payment Required"},
        {403, "Forbidden"},
        {404, "Not Found"},
        {405, "Method Not Allowed"},
        {406, "Not Acceptable"},
        {407, "Proxy Authentication Required"},
        {408, "Request Time-out"},
        {409, "Conflict"},
        {410, "Gone"},
        {411, "Length Required"},
        {412, "Precondition Failed"},
        {413, "Request Entity Too Large"},
        {414, "Request-URI Too Large"},
        {415, "Unsupported Media Type"},
        {416, "Requested range not satisfiable"},
        {417, "Expectation Failed"},
        {500, "Internal Server Error"},
        {501, "Not Implemented	"},
        {502, "Bad Gateway"},
        {503, "Service Unavailable"},
        {504, "Gateway Time-out"},
        {505, "HTTP Version not supported"}};
    auto it = _statu_msg.find(statu);
    if (it != _statu_msg.end())
    {
        return it->second;
    }
    return "Unknow";
}
static std::string ExtMime(const std::string &filename) // 文件后缀获取mime
{
    std::unordered_map<std::string, std::string> _file_mimes = {
        {".apk", "application/vnd.android.package-archive"},
        {".avi", "video/x-msvideo"},
        {".buffer", "application/octet-stream"},
        {".cer", "application/pkix-cert"},
        {".chm", "application/vnd.ms-htmlhelp"},
        {".conf", "text/plain"},
        {".cpp", "text/x-c"},
        {".crt", "application/x-x509-ca-cert"},
        {".css", "text/css"},
        {".csv", "text/csv"},
        {".doc", "application/msword"},
        {".docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"},
        {".exe", "application/x-msdownload"},
        {".flac", "audio/x-flac"},
        {".flv", "video/x-flv"},
        {".gif", "image/gif"},
        {".h263", "video/h263"},
        {".h264", "video/h264"},
        {".htm", "text/html"},
        {".html", "text/html"},
        {".ico", "image/x-icon"},
        {".ini", "text/plain"},
        {".ink", "application/inkml+xml"},
        {".iso", "application/x-iso9660-image"},
        {".jar", "application/java-archive"},
        {".java", "text/x-java-source"},
        {".jpeg", "image/jpeg"},
        {".jpg", "image/jpeg"},
        {".js", "application/javascript"},
        {".json", "application/json"},
        {".json5", "application/json5"},
        {".jsx", "text/jsx"},
        {".list", "text/plain"},
        {".lnk", "application/x-ms-shortcut"},
        {".log", "text/plain"},
        {".m3u8", "application/vnd.apple.mpegurl"},
        {".manifest", "text/cache-manifest"},
        {".map", "application/json"},
        {".markdown", "text/x-markdown"},
        {".md", "text/x-markdown"},
        {".mov", "video/quicktime"},
        {".mp3", "audio/mpeg"},
        {".mp4", "video/mp4"},
        {".mpeg", "video/mpeg"},
        {".mpg", "video/mpeg"},
        {".msi", "application/x-msdownload"},
        {".ogg", "audio/ogg"},
        {".ogv", "video/ogg"},
        {".otf", "font/opentype"},
        {".pdf", "application/pdf"},
        {".png", "image/png"},
        {".ppt", "application/vnd.ms-powerpoint"},
        {".pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation"},
        {".psd", "image/vnd.adobe.photoshop"},
        {".rar", "application/x-rar-compressed"},
        {".rm", "application/vnd.rn-realmedia"},
        {".rmvb", "application/vnd.rn-realmedia-vbr"},
        {".roff", "text/troff"},
        {".sass", "text/x-sass"},
        {".scss", "text/x-scss"},
        {".sh", "application/x-sh"},
        {".sql", "application/x-sql"},
        {".svg", "image/svg+xml"},
        {".swf", "application/x-shockwave-flash"},
        {".tar", "application/x-tar"},
        {".text", "text/plain"},
        {".torrent", "application/x-bittorrent"},
        {".ttf", "application/x-font-ttf"},
        {".txt", "text/plain"},
        {".wav", "audio/x-wav"},
        {".webm", "video/webm"},
        {".wm", "video/x-ms-wm"},
        {".wma", "audio/x-ms-wma"},
        {".wmx", "video/x-ms-wmx"},
        {".woff", "application/font-woff"},
        {".woff2", "application/font-woff2"},
        {".wps", "application/vnd.ms-works"},
        {".xhtml", "application/xhtml+xml"},
        {".xls", "application/vnd.ms-excel"},
        {".xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"},
        {".xml", "application/xml"},
        {".xz", "application/x-xz"},
        {".yaml", "text/yaml"},
        {".yml", "text/yaml"},
        {".zip", "application/zip"}};
    size_t pos = filename.rfind('.');
    if (pos == std::string::npos)
        return "application/octet-stream";
    std::string mime = filename.substr(pos);
    auto it = _file_mimes.find(mime);
    if (it != _file_mimes.end())
    {
        return it->second;
    }
    return "application/octet-stream";
}
static bool IsDirectory(const std::string& filename) // 是否为目录文件
    {
        struct stat st;
        int ret = stat(filename.c_str(),&st);
        if(ret < 0){
            return false;
        }
        return S_ISDIR(st.st_mode);
    }
    static bool IsRegular(const std::string& filename) // 是否为普通文件
    {
        struct stat st;
        int ret = stat(filename.c_str(),&st);
        if(ret < 0){
            return false;
        }
        return S_ISREG(st.st_mode);
    }
    static bool VaildPath(const std::string& path) // http请求资源路径的有效性
    // /index.html -- 前面的/叫相对根目录 映射的是某个服务器的子目录
    // 客户端只能请求相对根目录中的资源，其他地方的资源不予理会
    // /../index.html 这个路径会跑到相对根目录之外，不合理，错误
    {
        // 思想：按照/进行路径分割，根据有多少子目录，计算目录深度，有多少层，深度只能为1
        std::vector<std::string> subdir;
        int level = 0;
        Split(path,"/",&subdir);
        for(auto& dir:subdir){
            if(dir == ".") continue;
            if(dir == ".."){
                level--; // 任意一层小于0，认为有问题
                if(level < 0)
                    return false;
                continue;
            }
            level++;
        }
        return true;
    }
int main(int argc, char const *argv[])
{
    // std::string src = "GET,/,,,,HTTP/1.1,";
    // std::vector<std::string> res;
    // std::string sep = ",";
    // Util::Split(src,sep,&res);
    // for(auto& e : res){
    //     printf("[%s]\n",e.c_str());
    // }
    // const std::string filename = "+";
    // const std::string filename2 = "Any.cxx";
    // std::string buf;
    // bool ret = ReadFile(filename,&buf);
    // if(ret){
    //     std::cout << buf << std::endl;
    // }else
    //     std::cout << "err" << std::endl;
    // ret = WriteFile(filename2,buf);
    // std::string str = "C+ +";
    // std::string res= UrlEncode(str,true);
    // printf("%s\n",res.c_str());
    // std::cout << UrlDecode(res,true) << std::endl;
    // std::cout << StatusDesc(200) << std::endl;
    // std::cout << StatusDesc(404) << std::endl;
    // std::cout << ExtMime("aaa.exe") << std::endl;
    // std::cout << ExtMime("aaa.zip") << std::endl;
    // std::cout << ExtMime("aaa.json") << std::endl;
    // std::cout << ExtMime("aaa.mp4") << std::endl;
    // std::cout << ExtMime("aaa.html") << std::endl;
    // std::cout << ExtMime("aaa.tar.gz") << std::endl;
    std::cout << VaildPath("./index.html") << std::endl;
    std::cout << VaildPath("../index.html") << std::endl;
    std::cout << VaildPath("./index/../../index.html") << std::endl;
    std::cout << VaildPath("./index/index.html") << std::endl;
    std::cout << VaildPath("../../index/index/index.html") << std::endl;
    return 0;
}
